#pragma once
#include "frame.h"
#include <vector>
#include <memory>
#include <atomic>
#include <string>

class FramePool final
{
public:
	FramePool(size_t spliceSize_, size_t spliceCnt_, size_t expandingCnt_ = 5);
	FramePool(const FramePool&) = delete;
	FramePool& operator=(const FramePool&) = delete;
	// the caller guarantees that the frame has been fully returned before FramePool free
	~FramePool();
	bool Init();
	bool Fini();
	FramePtr BorrowFrame();
	int BorrowFrames(std::vector<FramePtr>& frameVec, size_t need, size_t needCnt);
	void ReturnFrame(Frame* f);
	bool IsFull() const 
	{
		MUTEX_GUARD(mtx);
		return frameList.size() == spliceCnt; 
	}
	size_t GetSpliceSize() const { return spliceSize; }
	size_t GetRemainCnt() const
	{
		MUTEX_GUARD(mtx);
		return frameList.size(); 
	}
	size_t GetTotalRemainFrames() const;
	// ouput audit log
	std::string Audit() const;
private:
	bool Expanding();
	FramePtr CreateOutsideFrame();
	void FreeOutsideFrame(Frame* f) 
	{
		ASSERT(f != nullptr);
		delete f;
		outsideCnt--;
	}
	std::string AuditCurrent() const;
	size_t GetTotalPoolCnt() const;
	size_t GetTotalOutsideCnt() const;
	std::atomic_bool isFinished{ false };
	char* pool { nullptr };
	size_t totalSize{ 0 };
	std::vector<Frame*> frameList;
	size_t outsideCnt{ 0 };
	const size_t spliceSize;
	const size_t spliceCnt;
	const size_t expandingCnt;	// max allow expanding count
	FramePool* next{ nullptr };	// expanding pool
	MUTEX(mtx);
};
using FramePoolPtr = std::shared_ptr<FramePool>;
